#include <stdint.h>
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

typedef enum {
    CLOG_ERROR,
    CLOG_WARN,
    CLOG_INFO,
    CLOG_DEBUG,
} clog_log_level;

static FILE* log_file = NULL;
pthread_mutex_t tmp_buf_mutex = PTHREAD_MUTEX_INITIALIZER;

#define CLOG_FILE_PATH "/tmp/clog_system.log"
#define CLOG_LEVEL CLOG_DEBUG


static const char* get_current_time(void)
{
    static char time_str[20];
    time_t now = time(NULL);
    struct tm* tm_info = localtime(&now);
    strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info);
    return time_str;
}

static void clog_log_write(clog_log_level level, const char* format, va_list args)
{
    const char* level_str;
    switch (level) {
        case CLOG_DEBUG: level_str = "D"; break;
        case CLOG_INFO:  level_str = "I";  break;
        case CLOG_WARN:  level_str = "W";  break;
        case CLOG_ERROR: level_str = "E"; break;
        default:         level_str = "U";
    }

    va_list args_copy;
    va_copy(args_copy, args);

    pthread_mutex_lock(&tmp_buf_mutex);
    if(NULL == log_file) {
        log_file = fopen(CLOG_FILE_PATH, "w+");
        if(NULL == log_file) {
            fprintf(stderr, "failed to open log file: %s\n", strerror(errno));
            pthread_mutex_unlock(&tmp_buf_mutex);
            va_end(args_copy);
            return;
        }
    }

    fprintf(log_file, "[%s] [%s] ", level_str, get_current_time());
    vfprintf(log_file, format, args);
    fflush(log_file);

    fprintf(stdout, "[%s] [%s] ", level_str, get_current_time());
    vfprintf(stdout, format, args_copy);
    fflush(stdout);

    pthread_mutex_unlock(&tmp_buf_mutex);
    va_end(args_copy);
}

void clog_log_debug(const char* format, ...) {
    va_list args;
    va_start(args, format);
    if(CLOG_LEVEL >= CLOG_DEBUG) {
        clog_log_write(CLOG_DEBUG, format, args);
    }
    va_end(args);
}

void clog_log_info(const char* format, ...) {
    va_list args;
    va_start(args, format);
    if(CLOG_LEVEL >= CLOG_INFO) {
        clog_log_write(CLOG_INFO, format, args);
    }
    va_end(args);
}

void clog_log_warn(const char* format, ...) {
    va_list args;
    va_start(args, format);
    if(CLOG_LEVEL >= CLOG_WARN) {
        clog_log_write(CLOG_WARN, format, args);
    }
    va_end(args);
}

void clog_log_error(const char* format, ...) {
    va_list args;
    va_start(args, format);
    if(CLOG_LEVEL >= CLOG_ERROR) {
        clog_log_write(CLOG_ERROR, format, args);
    }
    va_end(args);
}